/*
 * Copyright The Dragonfly Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package downloader

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"

	"github.com/dragonflyoss/Dragonfly/dfget/config"
	"github.com/dragonflyoss/Dragonfly/dfget/util"
)

// BackDownloader downloads the file from file resource.
type BackDownloader struct {
	Cfg     *config.Config
	URL     string
	Target  string
	Md5     string
	TaskID  string
	Node    string
	Total   int64
	Success bool

	tempFileName string
	cleaned      bool
}

// Run starts to download the file.
func (bd *BackDownloader) Run() error {
	var (
		resp *http.Response
		err  error
		f    *os.File
	)
	log := bd.Cfg.ClientLogger

	if bd.Cfg.Notbs || bd.Cfg.BackSourceReason == config.BackSourceReasonNoSpace {
		bd.Cfg.BackSourceReason += config.ForceNotBackSourceAddition
		err = fmt.Errorf("download fail and not back source: %d", bd.Cfg.BackSourceReason)
		return err
	}

	util.Printer.Printf("download from source")
	log.Infof("start download %s from the source station", path.Base(bd.Target))

	defer bd.Cleanup()

	prefix := "backsource." + bd.Cfg.Sign + "."
	if f, err = ioutil.TempFile(path.Dir(bd.Target), prefix); err != nil {
		return err
	}
	bd.tempFileName = f.Name()
	defer f.Close()

	if resp, err = httpGetWithHeaders(bd.URL, convertHeaders(bd.Cfg.Header)); err != nil {
		return err
	}
	defer resp.Body.Close()

	buf := make([]byte, 512*1024)
	reader := NewLimitReader(resp.Body, bd.Cfg.LocalLimit, bd.Md5 != "")
	if bd.Total, err = io.CopyBuffer(f, reader, buf); err != nil {
		return err
	}

	realMd5 := reader.Md5()
	if bd.Md5 == "" || bd.Md5 == realMd5 {
		err = moveFile(bd.tempFileName, bd.Target, "", bd.Cfg.ClientLogger)
	} else {
		err = fmt.Errorf("md5 not match, expected:%s real:%s", bd.Md5, realMd5)
	}
	bd.Success = err == nil
	return err
}

// Cleanup clean all temporary resources generated by executing Run.
func (bd *BackDownloader) Cleanup() {
	if bd.cleaned {
		return
	}

	if !util.IsEmptyStr(bd.tempFileName) {
		util.DeleteFile(bd.tempFileName)
	}
	bd.cleaned = true
}
